package com.codeanalysissys.backend.ruby;
/*** In The Name of Allah ***/

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Map;

import com.codeanalysissys.backend.python.parser.PythonLexer;
import com.codeanalysissys.backend.python.parser.PythonParser;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTree;
import com.codeanalysissys.backend.graphs.ast.ASNode;
import com.codeanalysissys.backend.graphs.ast.AbstractSyntaxTree;
import com.codeanalysissys.backend.ruby.parser.RubyParserBaseVisitor;

import com.codeanalysissys.backend.ruby.parser.RubyLexer;
import com.codeanalysissys.backend.ruby.parser.RubyParser;
import ghaffarian.nanologger.Logger;

import java.util.LinkedHashMap;

public class RubyASTBuilder {
    public static AbstractSyntaxTree build(String rubyFile) throws IOException {
        return build(new File(rubyFile));
    }

    public static AbstractSyntaxTree build(InputStream inFile) throws IOException {
        ANTLRInputStream input = new ANTLRInputStream(inFile);
        RubyLexer lexer = new RubyLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        RubyParser parser = new RubyParser(tokens);
        ParseTree tree = parser.compilation_unit();
        return build(tree, null, null);
    }

    public static AbstractSyntaxTree build(File rubyFile) throws IOException {
        if (!rubyFile.getName().endsWith(".rb"))
            throw new IOException("Not a ruby File!");
        //InputStream inFile = new FileInputStream(cppFile);
        // ANTLRInputStream input = new ANTLRInputStream(inFile);
        CharStream input = CharStreams.fromPath(Paths.get(rubyFile.getAbsolutePath()));
        RubyLexer lexer = new RubyLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        RubyParser parser = new RubyParser(tokens);
        ParseTree tree = parser.compilation_unit();
        //System.out.print(tree);
        return build(rubyFile.getName(), tree, null, null);
    }

    public static AbstractSyntaxTree build(String fileName, InputStream inputStream) throws IOException {

        InputStream inFile = inputStream;
        ANTLRInputStream input = new ANTLRInputStream(inFile);
        RubyLexer lexer = new RubyLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        RubyParser parser = new RubyParser(tokens);
        ParseTree tree = parser.compilation_unit();
        return build(fileName, tree, null, null);
    }

    public static AbstractSyntaxTree build(String filePath, ParseTree tree,
                                           String propKey, Map<ParserRuleContext, Object> ctxProps) {
        RubyASTBuilder.AbstractSyntaxVisitor visitor = new RubyASTBuilder.AbstractSyntaxVisitor(filePath, propKey, ctxProps);         //采用Antlr的visitor遍历
        Logger.debug("Visitor building AST of: " + filePath);
        return visitor.build(tree);
    }

    public static AbstractSyntaxTree build(ParseTree tree, String propKey, Map<ParserRuleContext, Object> ctxProps) {
        RubyASTBuilder.AbstractSyntaxVisitor visitor = new RubyASTBuilder.AbstractSyntaxVisitor(propKey, ctxProps);         //采用Antlr的visitor遍历
        return visitor.build(tree);
    }

    private static class AbstractSyntaxVisitor extends RubyParserBaseVisitor<String> {
        //private boolean isclass=false;
        private String propKey;
        private String typeModifier;
        private String memberModifier;
        private boolean Done = false;
        private boolean ismethod = false;
        private boolean isIdent = false;
        private Deque<ASNode> parentStack;
        private final AbstractSyntaxTree AST;
        private Map<String, String> vars, class_fields, methods, Main, Gvars, var_field;
        private int varsCounter, class_fieldsCounter, methodsCounter, mainCounter, GvarsCount;
        private Map<ParserRuleContext, Object> contexutalProperties;

        public AbstractSyntaxVisitor(String filePath, String propKey, Map<ParserRuleContext, Object> ctxProps) {
            parentStack = new ArrayDeque<>();
            AST = new AbstractSyntaxTree(filePath);
            this.propKey = propKey;
            contexutalProperties = ctxProps;
            Gvars = new LinkedHashMap<>();
            vars = new LinkedHashMap<>();
            class_fields = new LinkedHashMap<>();
            methods = new LinkedHashMap<>();
            Main = new LinkedHashMap<>();
            varsCounter = 0;
            class_fieldsCounter = 0;
            methodsCounter = 0;
            mainCounter = 0;
            GvarsCount = 0;
        }

        public AbstractSyntaxVisitor(String propKey, Map<ParserRuleContext, Object> ctxProps) {
            parentStack = new ArrayDeque<>();
            AST = new AbstractSyntaxTree();
            this.propKey = propKey;
            contexutalProperties = ctxProps;
            Gvars = new LinkedHashMap<>();
            vars = new LinkedHashMap<>();
            class_fields = new LinkedHashMap<>();
            methods = new LinkedHashMap<>();
            Main = new LinkedHashMap<>();
            varsCounter = 0;
            class_fieldsCounter = 0;
            methodsCounter = 0;
            mainCounter = 0;
            GvarsCount = 0;
        }

        public AbstractSyntaxTree build(ParseTree tree) {
            RubyParser.Compilation_unitContext rootCntx = (RubyParser.Compilation_unitContext) tree;
            AST.root.setCode(AST.filePath != null ? new File(AST.filePath).getName() : "root");
            parentStack.push(AST.root);
//            if (rootCntx.packageDeclaration() != null)
//                visit(rootCntx.packageDeclaration());
            //
//            if (rootCntx.importDeclaration() != null && rootCntx.importDeclaration().size() > 0) {
//                ASNode imports = new ASNode(ASNode.Type.IMPORTS);
//                imports.setLineOfCode(rootCntx.importDeclaration(0).getStart().getLine());
//                Logger.debug("Adding imports");
//                AST.addVertex(imports);
//                AST.addEdge(AST.root, imports);
//                parentStack.push(imports);
//                for (JavaParser.ImportDeclarationContext importCtx : rootCntx.importDeclaration())
//                    visit(importCtx);
//                parentStack.pop();
//            }
            ASNode main = new ASNode(ASNode.Type.MAIN);
            AST.addVertex(main);
            AST.addEdge(AST.root, main);
            if (rootCntx.statement_list_terms() != null) {
                parentStack.push(main);
                visit(rootCntx.statement_list_terms());
                parentStack.pop();
            }
            parentStack.pop();
            vars.clear();
            Main.clear();
            class_fields.clear();
            methods.clear();
            return AST;
        }

        @Override
        public String visitImports(RubyParser.ImportsContext ctx) {
            ASNode imports = new ASNode(ASNode.Type.IMPORTS);
            //imports.setCode(ctx.getText());
            imports.setLineOfCode(ctx.getStart().getLine());
            Logger.debug("Adding imports");
            AST.addVertex(imports);
            AST.addEdge(AST.root, imports);
            parentStack.push(imports);
            for (int i = 0; i < ctx.primary().size(); i++) {

                ASNode tem = new ASNode(ASNode.Type.IMPORT);
                tem.setCode(ctx.primary(i).getText());
                tem.setLineOfCode(ctx.primary(i).getStart().getLine());

                AST.addVertex(tem);
                AST.addEdge(parentStack.peek(), tem);
            }
            parentStack.pop();
            Done = true;
            return null;
        }

        @Override
        public String visitCase_body(RubyParser.Case_bodyContext ctx) {
            if (ctx.WHEN() != null) {
                ASNode when = new ASNode(ASNode.Type.WHEN);
                when.setCode("when");
                when.setLineOfCode(ctx.WHEN().getSymbol().getLine());
                AST.addVertex(when);
                AST.addEdge(parentStack.peek(), when);
                parentStack.push(when);
                ASNode cond = new ASNode(ASNode.Type.CONDITION);
                AST.addVertex(cond);
                AST.addEdge(parentStack.peek(), cond);
                parentStack.push(cond);
                visit(ctx.when_cond(0));
                parentStack.pop();
                ASNode then = new ASNode(ASNode.Type.THEN);
                AST.addVertex(then);
                AST.addEdge(parentStack.peek(), then);
                parentStack.push(then);
                visit(ctx.statement_list_terms());
                parentStack.pop();
                parentStack.pop();
            } else {
                ASNode els = new ASNode(ASNode.Type.ELSE);
                AST.addVertex(els);
                AST.addEdge(parentStack.peek(), els);
                parentStack.push(els);
                visit(ctx.else_tail().statement_list_terms());
                parentStack.pop();
            }
            return null;
        }

        @Override
        public String visitPrimaryBlockCase1(RubyParser.PrimaryBlockCase1Context ctx) {
            ASNode blockcase = new ASNode(ASNode.Type.CASE);
            blockcase.setCode("case");
            blockcase.setLineOfCode(ctx.getStart().getLine());
            AST.addVertex(blockcase);
            AST.addEdge(parentStack.peek(), blockcase);
            parentStack.push(blockcase);
            ASNode cond = new ASNode(ASNode.Type.CONDITION);
            AST.addVertex(cond);
            AST.addEdge(parentStack.peek(), cond);
            parentStack.push(cond);
            if (ctx.DOLLAR() != null) {
                ASNode symbol1 = new ASNode(ASNode.Type.SYMBOL);
                symbol1.setCode("$");
                symbol1.setLineOfCode(ctx.DOLLAR().getSymbol().getLine());
                AST.addVertex(symbol1);
                AST.addEdge(parentStack.peek(), symbol1);
            }
            visit(ctx.statement_list_terms());
            parentStack.pop();
            if (ctx.case_body() != null) {
                for (int i = 0; i < ctx.case_body().size(); i++) {
                    visit(ctx.case_body(i));
                }
            }

            ASNode end = new ASNode(ASNode.Type.END);
            AST.addVertex(end);
            AST.addEdge(parentStack.peek(), end);
            parentStack.pop();
            return null;
        }

        @Override
        public String visitExprEqualTest(RubyParser.ExprEqualTestContext ctx) {
            ASNode op = new ASNode(ASNode.Type.OPERATOR);
            op.setLineOfCode(ctx.equalsOperator().getStart().getLine());
            op.setCode(ctx.equalsOperator().getStart().getText());
            AST.addVertex(op);
            AST.addEdge(parentStack.peek(), op);
            parentStack.push(op);
            visit(ctx.expr(0));
            visit(ctx.expr(1));
            parentStack.pop();
            return null;
        }

        @Override
        public String visitExprLogicalNot(RubyParser.ExprLogicalNotContext ctx) {
            ASNode op = new ASNode(ASNode.Type.OPERATOR);
            op.setLineOfCode(ctx.not().getStart().getLine());
            op.setCode(ctx.not().getStart().getText());
            AST.addVertex(op);
            AST.addEdge(parentStack.peek(), op);
            parentStack.push(op);
            visit(ctx.expr());
            parentStack.pop();
            return null;
        }

        @Override
        public String visitExprLogicalJoin(RubyParser.ExprLogicalJoinContext ctx) {
            ASNode op = new ASNode(ASNode.Type.OPERATOR);
            op.setLineOfCode(ctx.logicalOperator().getStart().getLine());
            op.setCode(ctx.logicalOperator().getStart().getText());
            AST.addVertex(op);
            AST.addEdge(parentStack.peek(), op);
            parentStack.push(op);
            visit(ctx.expr(0));
            visit(ctx.expr(1));
            parentStack.pop();
            return null;
        }

        @Override
        public String visitExprCompareLogical(RubyParser.ExprCompareLogicalContext ctx) {
            ASNode op = new ASNode(ASNode.Type.OPERATOR);
            op.setLineOfCode(ctx.compareOperator().getStart().getLine());
            op.setCode(ctx.compareOperator().getStart().getText());
            AST.addVertex(op);
            AST.addEdge(parentStack.peek(), op);
            parentStack.push(op);
            visit(ctx.expr(0));
            visit(ctx.expr(1));
            parentStack.pop();
            return null;
        }

        @Override
        public String visitBlock(RubyParser.BlockContext ctx) {
            ASNode block = new ASNode(ASNode.Type.BLOCK);
            AST.addVertex(block);
            AST.addEdge(parentStack.peek(), block);
            parentStack.push(block);
            ASNode left = new ASNode(ASNode.Type.SYMBOL);
            left.setCode(ctx.LEFT_PAREN().getText());
            left.setLineOfCode(ctx.LEFT_PAREN().getSymbol().getLine());
            AST.addVertex(left);
            AST.addEdge(parentStack.peek(), left);
            if (ctx.block_params() != null) {
                ASNode params = new ASNode(ASNode.Type.PARAMS);
                AST.addVertex(params);
                AST.addEdge(parentStack.peek(), params);
                parentStack.push(params);
                ASNode symbol1 = new ASNode(ASNode.Type.SYMBOL);
                symbol1.setCode(ctx.block_params().BIT_OR(0).getText());
                symbol1.setLineOfCode(ctx.block_params().BIT_OR(0).getSymbol().getLine());
                AST.addVertex(symbol1);
                AST.addEdge(parentStack.peek(), symbol1);
                visit(ctx.block_params().expr(0));
                ASNode symbol = new ASNode(ASNode.Type.SYMBOL);
                symbol.setCode(ctx.block_params().BIT_OR(1).getText());
                symbol.setLineOfCode(ctx.block_params().BIT_OR(1).getSymbol().getLine());
                AST.addVertex(symbol);
                AST.addEdge(parentStack.peek(), symbol);
                parentStack.pop();
            }
            visit(ctx.statement_list_terms());
            ASNode right = new ASNode(ASNode.Type.SYMBOL);
            right.setCode(ctx.RIGHT_PAREN().getText());
            right.setLineOfCode(ctx.RIGHT_PAREN().getSymbol().getLine());
            AST.addVertex(right);
            AST.addEdge(parentStack.peek(), right);
            return null;
        }

        @Override
        public String visitExprBlock(RubyParser.ExprBlockContext ctx) {
            ASNode method = new ASNode(ASNode.Type.METHOD_ADD_BLOCK);
            AST.addVertex(method);
            AST.addEdge(parentStack.peek(), method);
            parentStack.push(method);
            visit(ctx.expr());
            visit(ctx.block());
            parentStack.pop();
            return null;
        }

        @Override
        public String visitExpr_statement_suffix(RubyParser.Expr_statement_suffixContext ctx) {
            if (ctx.DO() == null) {
                ASNode key = new ASNode(ASNode.Type.TYPE);
                if (ctx.IF() != null) {
                    key = new ASNode(ASNode.Type.IF);
                } else if (ctx.UNLESS() != null) {
                    key = new ASNode(ASNode.Type.UNLESS);
                } else if (ctx.WHILE() != null) {
                    key = new ASNode(ASNode.Type.WHILE);
                } else {
                    key = new ASNode(ASNode.Type.UNTIL);
                }
                AST.addVertex(key);
                AST.addEdge(parentStack.peek(), key);
                parentStack.push(key);
                ASNode cond = new ASNode(ASNode.Type.CONDITION);
                AST.addVertex(cond);
                AST.addEdge(parentStack.peek(), cond);
                parentStack.push(cond);
                visit(ctx.expr());
                parentStack.pop();
            } else {
                ASNode doexp = new ASNode(ASNode.Type.METHOD_ADD_BLOCK);
                doexp.setCode("do");
                doexp.setLineOfCode(ctx.DO().getSymbol().getLine());
                AST.addVertex(doexp);
                AST.addEdge(parentStack.peek(), doexp);
                parentStack.push(doexp);
                if (ctx.block_params() != null) {
                    ASNode params = new ASNode(ASNode.Type.PARAMS);
                    AST.addVertex(params);
                    AST.addEdge(parentStack.peek(), params);
                    parentStack.push(params);
                    ASNode symbol1 = new ASNode(ASNode.Type.SYMBOL);
                    symbol1.setCode(ctx.block_params().BIT_OR(0).getText());
                    symbol1.setLineOfCode(ctx.block_params().BIT_OR(0).getSymbol().getLine());
                    AST.addVertex(symbol1);
                    AST.addEdge(parentStack.peek(), symbol1);
                    visit(ctx.block_params().expr(0));
                    ASNode symbol = new ASNode(ASNode.Type.SYMBOL);
                    symbol.setCode(ctx.block_params().BIT_OR(1).getText());
                    symbol.setLineOfCode(ctx.block_params().BIT_OR(1).getSymbol().getLine());
                    AST.addVertex(symbol);
                    AST.addEdge(parentStack.peek(), symbol);
                    parentStack.pop();
                }
                ASNode body = new ASNode(ASNode.Type.BODYSTMT);
                AST.addVertex(body);
                AST.addEdge(parentStack.peek(), body);
                parentStack.push(body);
                visit(ctx.statement_list_terms());
                parentStack.pop();
                ASNode end = new ASNode(ASNode.Type.END);
                AST.addVertex(end);
                AST.addEdge(parentStack.peek(), end);
                parentStack.pop();
            }
            return null;
        }

        @Override
        public String visitExprWitStatementSuffix(RubyParser.ExprWitStatementSuffixContext ctx) {
            if (ctx.expr_statement_suffix().DO() == null) {

                visit(ctx.expr_statement_suffix());
                ASNode keyword;
                if (ctx.expr_statement_suffix().IF() != null || ctx.expr_statement_suffix().UNLESS() != null) {
                    keyword = new ASNode(ASNode.Type.THEN);
                } else {
                    keyword = new ASNode(ASNode.Type.DO);
                }
                AST.addVertex(keyword);
                AST.addEdge(parentStack.peek(), keyword);
                parentStack.push(keyword);
                visit(ctx.expr());
                parentStack.pop();

                parentStack.pop();//弹出if
            } else {
                ASNode method = new ASNode(ASNode.Type.METHOD_ADD_BLOCK);
                AST.addVertex(method);
                AST.addEdge(parentStack.peek(), method);
                parentStack.push(method);
                visit(ctx.expr());
                visit(ctx.expr_statement_suffix());
                parentStack.pop();
            }
            return null;
        }

        @Override
        public String visitIf_tail(RubyParser.If_tailContext ctx) {
            ASNode els = new ASNode(ASNode.Type.ELSE);
            AST.addVertex(els);
            AST.addEdge(parentStack.peek(), els);
            parentStack.push(els);

            if (ctx.ELSIF() != null) {
                ASNode key = new ASNode(ASNode.Type.IF);
                AST.addVertex(key);
                AST.addEdge(parentStack.peek(), key);
                parentStack.push(key);
                ASNode cond = new ASNode(ASNode.Type.CONDITION);
                AST.addVertex(cond);
                AST.addEdge(parentStack.peek(), cond);
                parentStack.push(cond);
                visit(ctx.expr());
                parentStack.pop();
                ASNode then = new ASNode(ASNode.Type.THEN);
                AST.addVertex(then);
                AST.addEdge(parentStack.peek(), then);
                parentStack.push(then);
                visit(ctx.statement_list_terms());
                parentStack.pop();
            } else {
                visit(ctx.else_tail().statement_list_terms());
            }
            return null;
        }

        @Override
        public String visitPrimaryBlockUnless(RubyParser.PrimaryBlockUnlessContext ctx) {
            ASNode key = new ASNode(ASNode.Type.UNLESS);
            AST.addVertex(key);
            AST.addEdge(parentStack.peek(), key);
            parentStack.push(key);
            ASNode cond = new ASNode(ASNode.Type.CONDITION);
            AST.addVertex(cond);
            AST.addEdge(parentStack.peek(), cond);
            parentStack.push(cond);
            visit(ctx.expr());
            parentStack.pop();
            ASNode then = new ASNode(ASNode.Type.THEN);
            AST.addVertex(then);
            AST.addEdge(parentStack.peek(), then);
            parentStack.push(then);
            visit(ctx.statement_list_terms());
            parentStack.pop();
            if (ctx.else_tail() != null) {
                ASNode els = new ASNode(ASNode.Type.ELSE);
                AST.addVertex(els);
                AST.addEdge(parentStack.peek(), els);
                parentStack.push(els);
                visit(ctx.else_tail().statement_list_terms());
                parentStack.pop();
            }
            ASNode end = new ASNode(ASNode.Type.END);
            AST.addVertex(end);
            AST.addEdge(parentStack.peek(), end);
            parentStack.pop();
            return null;
        }

        @Override
        public String visitPrimaryBlockIf(RubyParser.PrimaryBlockIfContext ctx) {
            ASNode key = new ASNode(ASNode.Type.IF);
            AST.addVertex(key);
            AST.addEdge(parentStack.peek(), key);
            parentStack.push(key);
            ASNode cond = new ASNode(ASNode.Type.CONDITION);
            AST.addVertex(cond);
            AST.addEdge(parentStack.peek(), cond);
            parentStack.push(cond);
            visit(ctx.expr());
            parentStack.pop();


            ASNode then = new ASNode(ASNode.Type.THEN);
            AST.addVertex(then);
            AST.addEdge(parentStack.peek(), then);
            parentStack.push(then);
            visit(ctx.statement_list_terms());
            parentStack.pop();
            if (ctx.if_tail() != null) {
                int i;
                for (i = 0; i < ctx.if_tail().size(); i++)
                    visit(ctx.if_tail(i));
                if (i > 0) {
                    if (ctx.if_tail(i - 1).else_tail() != null)
                        i = i * 2 - 1;
                    else
                        i = i * 2;
                }
                for (; i > 0; i--) {
                    parentStack.pop();
                }
            }
            ASNode end = new ASNode(ASNode.Type.END);
            AST.addVertex(end);
            AST.addEdge(parentStack.peek(), end);
            parentStack.pop();
            return null;
        }

        @Override
        public String visitPrimaryBlockUntil(RubyParser.PrimaryBlockUntilContext ctx) {
            ASNode until = new ASNode(ASNode.Type.UNTIL);
            until.setLineOfCode(ctx.getStart().getLine());
            AST.addVertex(until);
            AST.addEdge(parentStack.peek(), until);
            parentStack.push(until);
            ASNode cond = new ASNode(ASNode.Type.CONDITION);
            AST.addVertex(cond);
            AST.addEdge(parentStack.peek(), cond);
            parentStack.push(cond);
            visit(ctx.expr());
            parentStack.pop();
            ASNode DO = new ASNode(ASNode.Type.DO);
            AST.addVertex(DO);
            AST.addEdge(parentStack.peek(), DO);
            parentStack.push(DO);
            visit(ctx.statement_list_terms());
            parentStack.pop();
            ASNode end = new ASNode(ASNode.Type.END);
            AST.addVertex(end);
            AST.addEdge(parentStack.peek(), end);
            parentStack.pop();
            return null;
        }

        @Override
        public String visitPrimaryStatementYield(RubyParser.PrimaryStatementYieldContext ctx) {
            ASNode yieldNode = new ASNode(ASNode.Type.YIELD);
            yieldNode.setLineOfCode(ctx.getStart().getLine());
            AST.addVertex(yieldNode);
            AST.addEdge(parentStack.peek(), yieldNode);
            parentStack.push(yieldNode);
            ASNode call = new ASNode(ASNode.Type.FUNCTION_CALL);
            AST.addVertex(call);
            AST.addEdge(parentStack.peek(), call);
            parentStack.push(call);

            visit(ctx.expr());
            parentStack.pop();
            parentStack.pop();
            return null;
        }

        @Override
        public String visitPrimaryStatementRaise(RubyParser.PrimaryStatementRaiseContext ctx) {
            ASNode command = new ASNode(ASNode.Type.COMMAND);
            command.setLineOfCode(ctx.getStart().getLine());
            AST.addVertex(command);
            AST.addEdge(parentStack.peek(), command);
            parentStack.push(command);
            ASNode raise = new ASNode(ASNode.Type.RAISE);
            raise.setLineOfCode(ctx.getStart().getLine());
            AST.addVertex(raise);
            AST.addEdge(parentStack.peek(), raise);

            ASNode body = new ASNode(ASNode.Type.BODYSTMT);
            body.setLineOfCode(ctx.getStart().getLine());
            AST.addVertex(body);
            AST.addEdge(parentStack.peek(), body);
            parentStack.push(body);

            visit(ctx.expr());
            parentStack.pop();
            parentStack.pop();
            return null;
        }

        @Override
        public String visitPrimaryBlockFor(RubyParser.PrimaryBlockForContext ctx) {
            ASNode whileexp = new ASNode(ASNode.Type.FOR);
            whileexp.setLineOfCode(ctx.getStart().getLine());
            AST.addVertex(whileexp);
            AST.addEdge(parentStack.peek(), whileexp);
            parentStack.push(whileexp);
            ASNode temp_var = new ASNode(ASNode.Type.VAR_FIELD);
            AST.addVertex(temp_var);
            AST.addEdge(parentStack.peek(), temp_var);
            parentStack.push(temp_var);
            visit(ctx.expr());
            parentStack.pop();
            visit(ctx.when_cond());
            ASNode body = new ASNode(ASNode.Type.BODYSTMT);
            AST.addVertex(body);
            AST.addEdge(parentStack.peek(), body);
            parentStack.push(body);
            visit(ctx.statement_list_terms());
            parentStack.pop();
            ASNode end = new ASNode(ASNode.Type.END);
            AST.addVertex(end);
            AST.addEdge(parentStack.peek(), end);
            parentStack.pop();
            return null;
        }

        @Override
        public String visitPrimaryBlockWhile(RubyParser.PrimaryBlockWhileContext ctx) {
            ASNode whileexp = new ASNode(ASNode.Type.WHILE);
            whileexp.setLineOfCode(ctx.getStart().getLine());
            AST.addVertex(whileexp);
            AST.addEdge(parentStack.peek(), whileexp);
            parentStack.push(whileexp);
            ASNode cond = new ASNode(ASNode.Type.CONDITION);
            AST.addVertex(cond);
            AST.addEdge(parentStack.peek(), cond);
            parentStack.push(cond);
            visit(ctx.expr());
            parentStack.pop();
            ASNode DO = new ASNode(ASNode.Type.DO);
            AST.addVertex(DO);
            AST.addEdge(parentStack.peek(), DO);
            parentStack.push(DO);
            visit(ctx.statement_list_terms());
            parentStack.pop();
            ASNode end = new ASNode(ASNode.Type.END);
            AST.addVertex(end);
            AST.addEdge(parentStack.peek(), end);
            parentStack.pop();
            return null;
        }

        @Override
        public String visitPrimaryBeginBlock(RubyParser.PrimaryBeginBlockContext ctx) {
            ASNode begin = new ASNode(ASNode.Type.BEGIN);
            begin.setLineOfCode(ctx.getStart().getLine());
            AST.addVertex(begin);
            AST.addEdge(parentStack.peek(), begin);
            parentStack.push(begin);
            ASNode body = new ASNode(ASNode.Type.BODYSTMT);
            AST.addVertex(body);
            AST.addEdge(parentStack.peek(), body);
            parentStack.push(body);
            visit(ctx.statement_list_terms(0));
            if (ctx.rescuestatement() != null) {
                ASNode rescue = new ASNode(ASNode.Type.RESCUE);
                rescue.setLineOfCode(ctx.getStart().getLine());
                AST.addVertex(rescue);
                AST.addEdge(parentStack.peek(), rescue);
                parentStack.push(rescue);
                visit(ctx.rescuestatement().statement_list_terms());
                if (ctx.rescuestatement().else_tail() != null) {
                    ASNode els = new ASNode(ASNode.Type.ELSE);
                    els.setLineOfCode(ctx.getStart().getLine());
                    AST.addVertex(els);
                    AST.addEdge(parentStack.peek(), els);
                    parentStack.push(els);
                    visit(ctx.rescuestatement().else_tail().statement_list_terms());
                    parentStack.pop();
                }
                parentStack.pop();
            }
            if (ctx.ensure() != null) {
                ASNode ensure = new ASNode(ASNode.Type.ENSURE);
                ensure.setLineOfCode(ctx.getStart().getLine());
                AST.addVertex(ensure);
                AST.addEdge(parentStack.peek(), ensure);
                parentStack.push(ensure);
                visit(ctx.statement_list_terms(1));
                parentStack.pop();
            }
            parentStack.pop();
            parentStack.pop();
            return null;
        }

        @Override
        public String visitCpath(RubyParser.CpathContext ctx) {
            if (!ctx.DOT().isEmpty()) {
                for (int i = ctx.DOT().size() - 1; i >= 0; i--) {
                    ASNode call = new ASNode(ASNode.Type.FUNCTION_CALL);
                    AST.addVertex(call);
                    AST.addEdge(parentStack.peek(), call);
                    parentStack.push(call);
                    if (i == 0) {
                        ASNode ident0 = new ASNode(ASNode.Type.IDENT);
                        ident0.setLineOfCode(ctx.identifier(i).getStart().getLine());
                        ident0.setCode(ctx.identifier(i).getText());
                        AST.addVertex(ident0);
                        AST.addEdge(parentStack.peek(), ident0);
                    }

                    ASNode dot = new ASNode(ASNode.Type.PERIOD);
                    dot.setLineOfCode(ctx.DOT(i).getSymbol().getLine());
                    dot.setCode(".");
                    AST.addVertex(dot);
                    AST.addEdge(parentStack.peek(), dot);
                    ASNode ident = new ASNode(ASNode.Type.IDENT);
                    ident.setLineOfCode(ctx.identifier(i + 1).getStart().getLine());
                    ident.setCode(ctx.identifier(i + 1).getText());
                    AST.addVertex(ident);
                    AST.addEdge(parentStack.peek(), ident);

                }
                for (int i = ctx.DOT().size() - 1; i >= 0; i--) {
                    parentStack.pop();
                }
            } else {
                ASNode ident0 = new ASNode(ASNode.Type.IDENT);
                ident0.setLineOfCode(ctx.identifier(0).getStart().getLine());
                ident0.setCode(ctx.identifier(0).getText());
                AST.addVertex(ident0);
                AST.addEdge(parentStack.peek(), ident0);
            }
            return null;
        }

        @Override
        public String visitExprDotRef(RubyParser.ExprDotRefContext ctx) {
            isIdent = true;
            ASNode call = new ASNode(ASNode.Type.FUNCTION_CALL);
            AST.addVertex(call);
            AST.addEdge(parentStack.peek(), call);
            parentStack.push(call);
            visit(ctx.expr(0));

            ASNode dot = new ASNode(ASNode.Type.PERIOD);
            dot.setLineOfCode(ctx.dot_ref().getStart().getLine());
            dot.setCode(".");
            AST.addVertex(dot);
            AST.addEdge(parentStack.peek(), dot);
            isIdent = true;
            visit(ctx.expr(1));

            parentStack.pop();
            isIdent = false;
            return null;
        }

        @Override
        public String visitPrimaryFunctionCall0(RubyParser.PrimaryFunctionCall0Context ctx) {
            ASNode call = new ASNode(ASNode.Type.FUNCTION_CALL);
            AST.addVertex(call);
            AST.addEdge(parentStack.peek(), call);
            parentStack.push(call);

            visit(ctx.function_name());

            ASNode params = new ASNode(ASNode.Type.PARAMS);
            AST.addVertex(params);
            AST.addEdge(parentStack.peek(), params);
            parentStack.push(params);

            ASNode symbol1 = new ASNode(ASNode.Type.SYMBOL);
            symbol1.setCode(ctx.func_call_parameters().LEFT_RBRACKET().getText());
            symbol1.setLineOfCode(ctx.func_call_parameters().LEFT_RBRACKET().getSymbol().getLine());
            AST.addVertex(symbol1);
            AST.addEdge(parentStack.peek(), symbol1);
            if (ctx.func_call_parameters() != null) {
                for (int i = 0; i < ctx.func_call_parameters().function_call_param().size(); i++)
                    visit(ctx.func_call_parameters().function_call_param(i));
            }

            ASNode symbol2 = new ASNode(ASNode.Type.SYMBOL);
            symbol2.setCode(ctx.func_call_parameters().RIGHT_RBRACKET().getText());
            symbol2.setLineOfCode(ctx.func_call_parameters().RIGHT_RBRACKET().getSymbol().getLine());
            AST.addVertex(symbol2);
            AST.addEdge(parentStack.peek(), symbol2);
            parentStack.pop();
            parentStack.pop();
            return null;

        }

        @Override
        public String visitPrimaryListExpr(RubyParser.PrimaryListExprContext ctx) {
            ASNode list = new ASNode(ASNode.Type.ARRAY);
            AST.addVertex(list);
            AST.addEdge(parentStack.peek(), list);
            parentStack.push(list);

            ASNode symbol1 = new ASNode(ASNode.Type.SYMBOL);
            symbol1.setCode(ctx.LEFT_SBRACKET().getText());
            symbol1.setLineOfCode(ctx.LEFT_SBRACKET().getSymbol().getLine());
            AST.addVertex(symbol1);
            AST.addEdge(parentStack.peek(), symbol1);

            visit(ctx.expr(0));

            ASNode symbol2 = new ASNode(ASNode.Type.SYMBOL);
            symbol2.setCode(ctx.RIGHT_SBRACKET().getText());
            symbol2.setLineOfCode(ctx.RIGHT_SBRACKET().getSymbol().getLine());
            AST.addVertex(symbol2);
            AST.addEdge(parentStack.peek(), symbol2);
            parentStack.pop();
            return null;
        }

        @Override
        public String visitExprList(RubyParser.ExprListContext ctx) {
            visit(ctx.expr(0));
            ASNode symbol = new ASNode(ASNode.Type.SYMBOL);
            symbol.setCode(ctx.COMMA().getText());
            symbol.setLineOfCode(ctx.COMMA().getSymbol().getLine());
            AST.addVertex(symbol);
            AST.addEdge(parentStack.peek(), symbol);
            visit(ctx.expr(1));
            return null;
        }

        @Override
        public String visitPrimaryRetry(RubyParser.PrimaryRetryContext ctx) {
            ASNode retry = new ASNode(ASNode.Type.RETRY);
            retry.setLineOfCode(ctx.getStart().getLine());
            AST.addVertex(retry);
            AST.addEdge(parentStack.peek(), retry);
            return null;
        }

        @Override
        public String visitNext(RubyParser.NextContext ctx) {
            ASNode next = new ASNode(ASNode.Type.NEXT);
            next.setLineOfCode(ctx.getStart().getLine());
            AST.addVertex(next);
            AST.addEdge(parentStack.peek(), next);
            Done = true;
            return null;
        }

        @Override
        public String visitPrimaryStatementBreak(RubyParser.PrimaryStatementBreakContext ctx) {
            ASNode breakexp = new ASNode(ASNode.Type.BREAK);
            breakexp.setLineOfCode(ctx.getStart().getLine());
            AST.addVertex(breakexp);
            AST.addEdge(parentStack.peek(), breakexp);
            return null;
        }

        @Override
        public String visitBegin_block(RubyParser.Begin_blockContext ctx) {
            ASNode BG = new ASNode(ASNode.Type.BEGIN);
            BG.setLineOfCode(ctx.getStart().getLine());
            AST.addVertex(BG);
            AST.addEdge(parentStack.peek(), BG);
            parentStack.push(BG);
            visit(ctx.statement_list_terms());
            parentStack.pop();
            return null;
        }

        @Override
        public String visitEnd_block(RubyParser.End_blockContext ctx) {
            ASNode ED = new ASNode(ASNode.Type.END);
            ED.setLineOfCode(ctx.getStart().getLine());
            AST.addVertex(ED);
            AST.addEdge(parentStack.peek(), ED);
            parentStack.push(ED);
            visit(ctx.statement_list_terms());
            parentStack.pop();
            return null;
        }

        @Override
        public String visitFunction_definition(RubyParser.Function_definitionContext ctx) {
            ismethod = true;
            ASNode def = new ASNode(ASNode.Type.DEF);
            def.setLineOfCode(ctx.getStart().getLine());
            AST.addVertex(def);
            AST.addEdge(parentStack.peek(), def);
            parentStack.push(def);
            if (ctx.function_definition_header().function_name().cpath().DOT().isEmpty()) {
                ASNode name = new ASNode(ASNode.Type.METHOD);
                name.setCode(ctx.function_definition_header().function_name().getText());
                name.setLineOfCode(ctx.function_definition_header().function_name().getStart().getLine());
                AST.addVertex(name);
                AST.addEdge(parentStack.peek(), name);
                methodsCounter++;
                String normalized = "$METHOD_" + methodsCounter;
                name.setNormalizedCode(normalized);
                methods.put(ctx.function_definition_header().function_name().getText(), normalized);
            } else {
                visit(ctx.function_definition_header().function_name());
            }
            ASNode params = new ASNode(ASNode.Type.PARAMS);
            AST.addVertex(params);
            AST.addEdge(parentStack.peek(), params);
            parentStack.push(params);
            if (ctx.function_definition_header().function_definition_params() != null) {
                for (int i = 0; i < ctx.function_definition_header().function_definition_params().function_definition_param().size(); i++) {
                    ASNode params_name = new ASNode(ASNode.Type.VARIABLE);
                    params_name.setCode(ctx.function_definition_header().function_definition_params().function_definition_param(i).getText());
                    params_name.setLineOfCode(ctx.function_definition_header().function_definition_params().function_definition_param(i).getStart().getLine());
                    AST.addVertex(params_name);
                    AST.addEdge(parentStack.peek(), params_name);
                    ++varsCounter;
                    String normalize = "$VARL_" + varsCounter;
                    vars.put(ctx.function_definition_header().function_definition_params().function_definition_param(i).getText(), normalize);
                    params_name.setNormalizedCode(normalize);
                }
            }
            parentStack.pop();
            ASNode body = new ASNode(ASNode.Type.BODYSTMT);
            AST.addVertex(body);
            AST.addEdge(parentStack.peek(), body);
            parentStack.push(body);
            visit(ctx.statement_list_terms());
            parentStack.pop();
            parentStack.pop();
            ismethod = false;
            return null;
        }

        @Override
        public String visitModule_definition(RubyParser.Module_definitionContext ctx) {
            ASNode modu = new ASNode(ASNode.Type.MODULE);
            modu.setLineOfCode(ctx.getStart().getLine());
            AST.addVertex(modu);
            AST.addEdge(parentStack.peek(), modu);
            parentStack.push(modu);

            ASNode con = new ASNode(ASNode.Type.CONST_REF);
            AST.addVertex(con);
            AST.addEdge(parentStack.peek(), con);
            parentStack.push(con);

            ASNode name = new ASNode(ASNode.Type.CONST);
            name.setCode(ctx.cpath().getText());
            name.setLineOfCode(ctx.cpath().getStart().getLine());
            AST.addVertex(name);
            AST.addEdge(parentStack.peek(), name);
            parentStack.pop();
            ASNode body = new ASNode(ASNode.Type.BODYSTMT);
            AST.addVertex(body);
            AST.addEdge(parentStack.peek(), body);
            parentStack.push(body);
            visit(ctx.statement_list_terms());
            parentStack.pop();
            parentStack.pop();
            return null;
        }

        @Override
        public String visitClass_definition(RubyParser.Class_definitionContext ctx) {
            if (ctx.class_header().BIT_SHL() == null) {
                ASNode cls = new ASNode(ASNode.Type.CLASS);
                cls.setLineOfCode(ctx.getStart().getLine());
                AST.addVertex(cls);
                AST.addEdge(AST.root, cls);
                parentStack.push(cls);
                ASNode con = new ASNode(ASNode.Type.CONST_REF);
                AST.addVertex(con);
                AST.addEdge(parentStack.peek(), con);
                parentStack.push(con);

                ASNode name = new ASNode(ASNode.Type.CONST);
                name.setCode(ctx.class_header().cpath().getText());
                name.setLineOfCode(ctx.class_header().cpath().getStart().getLine());
                AST.addVertex(name);
                AST.addEdge(parentStack.peek(), name);
                parentStack.pop();
                class_fieldsCounter++;
                String normalize = "$CLASS_" + class_fieldsCounter;
                class_fields.put(ctx.class_header().cpath().getText(), normalize);
                if (ctx.class_header().superclass() != null) {
                    ASNode var = new ASNode(ASNode.Type.VAR_REF);
                    AST.addVertex(var);
                    AST.addEdge(parentStack.peek(), var);
                    parentStack.push(var);
                    visit(ctx.class_header().superclass());
                    parentStack.pop();
                }
                ASNode body = new ASNode(ASNode.Type.BODYSTMT);
                AST.addVertex(body);
                AST.addEdge(parentStack.peek(), body);
                parentStack.push(body);
                visit(ctx.statement_list_terms());
                parentStack.pop();
                parentStack.pop();
            } else {
                ASNode cls = new ASNode(ASNode.Type.SCLASS);
                cls.setLineOfCode(ctx.getStart().getLine());
                AST.addVertex(cls);
                AST.addEdge(AST.root, cls);
                parentStack.push(cls);
                ASNode var = new ASNode(ASNode.Type.VAR_REF);
                AST.addVertex(var);
                AST.addEdge(parentStack.peek(), var);
                parentStack.push(var);

                ASNode name = new ASNode(ASNode.Type.VARIABLE);
                name.setCode(ctx.class_header().identifier().getText());
                name.setLineOfCode(ctx.class_header().identifier().getStart().getLine());
                AST.addVertex(name);
                AST.addEdge(parentStack.peek(), name);

                parentStack.pop();
                ASNode body = new ASNode(ASNode.Type.BODYSTMT);
                AST.addVertex(body);
                AST.addEdge(parentStack.peek(), body);
                parentStack.push(body);
                visit(ctx.statement_list_terms());
                parentStack.pop();
                parentStack.pop();
            }
            return null;
        }

        @Override
        public String visitPrimaryStatementReturn(RubyParser.PrimaryStatementReturnContext ctx) {
            ASNode re = new ASNode(ASNode.Type.RETURN);
            re.setLineOfCode(ctx.getStart().getLine());
            AST.addVertex(re);
            AST.addEdge(parentStack.peek(), re);
            parentStack.push(re);
            if (ctx.expr() != null) {
                visit(ctx.expr());
            }
            parentStack.pop();
            return null;
        }

        @Override
        public String visitExprPrint(RubyParser.ExprPrintContext ctx) {
            ASNode func = new ASNode(ASNode.Type.FUNCTION_CALL);
            AST.addVertex(func);
            AST.addEdge(parentStack.peek(), func);
            parentStack.push(func);

            ASNode print = new ASNode(ASNode.Type.FUNCTION);
            print.setLineOfCode(ctx.print().getStart().getLine());
            print.setCode(ctx.print().getText());
            AST.addVertex(print);
            AST.addEdge(parentStack.peek(), print);

            ASNode params = new ASNode(ASNode.Type.PARAMS);
            AST.addVertex(params);
            AST.addEdge(parentStack.peek(), params);
            parentStack.push(params);
//            for(int i=0;i<ctx.primary().size();i++) {
//                visit(ctx.primary(i));
//                if(i<ctx.COMMA().size())
//                {
//                    ASNode symbol=new ASNode(ASNode.Type.SYMBOL);
//                    symbol.setCode(ctx.COMMA(i).getText());
//                    symbol.setLineOfCode(ctx.COMMA(i).getSymbol().getLine());
//                    AST.addVertex(symbol);
//                    AST.addEdge(parentStack.peek(),symbol);
//                }
//            }
            visit(ctx.expr());
            parentStack.pop();
            parentStack.pop();
            return null;
        }

        @Override
        public String visitExprCalcuation(RubyParser.ExprCalcuationContext ctx) {
            ASNode op = new ASNode(ASNode.Type.OPERATOR);
            op.setLineOfCode(ctx.mathOperator().getStart().getLine());
            op.setCode(ctx.mathOperator().getStart().getText());
            AST.addVertex(op);
            AST.addEdge(parentStack.peek(), op);
            parentStack.push(op);
            visit(ctx.expr(0));
            visit(ctx.expr(1));
            parentStack.pop();
            return null;
        }

        @Override
        public String visitExprArrayAccess(RubyParser.ExprArrayAccessContext ctx) {
            ASNode array = new ASNode(ASNode.Type.AREF_FIELD);
            AST.addVertex(array);
            AST.addEdge(parentStack.peek(), array);
            parentStack.push(array);
            visit(ctx.expr(0));
            ASNode symbol = new ASNode(ASNode.Type.SYMBOL);
            symbol.setCode(ctx.LEFT_SBRACKET().getText());
            symbol.setLineOfCode(ctx.LEFT_SBRACKET().getSymbol().getLine());
            AST.addVertex(symbol);
            AST.addEdge(parentStack.peek(), symbol);
            visit(ctx.expr(1));
            ASNode symbol2 = new ASNode(ASNode.Type.SYMBOL);
            symbol2.setCode(ctx.RIGHT_SBRACKET().getText());
            symbol2.setLineOfCode(ctx.RIGHT_SBRACKET().getSymbol().getLine());
            AST.addVertex(symbol2);
            AST.addEdge(parentStack.peek(), symbol2);
            parentStack.pop();
            return null;
        }

        @Override
        public String visitAlias_statement(RubyParser.Alias_statementContext ctx) {
            ASNode alias = new ASNode(ASNode.Type.ALIAS);
            alias.setLineOfCode(ctx.ALIAS().getSymbol().getLine());
            AST.addVertex(alias);
            AST.addEdge(parentStack.peek(), alias);
            parentStack.push(alias);
            ASNode sy = new ASNode(ASNode.Type.SYMBOL_LITERAL);
            AST.addVertex(sy);
            AST.addEdge(parentStack.peek(), sy);
            parentStack.push(sy);
            visit(ctx.function_name_or_symbol(0));
            parentStack.pop();
            AST.addEdge(parentStack.peek(), sy);
            parentStack.push(sy);
            visit(ctx.function_name_or_symbol(1));
            parentStack.pop();
            parentStack.pop();
            return null;
        }

        @Override
        public String visitUndef_statement(RubyParser.Undef_statementContext ctx) {
            ASNode undef = new ASNode(ASNode.Type.UNDEF);
            undef.setLineOfCode(ctx.UNDEF().getSymbol().getLine());
            AST.addVertex(undef);
            AST.addEdge(parentStack.peek(), undef);
            parentStack.push(undef);
            for (int i = 0; i < ctx.function_name_or_symbol().size(); i++) {
                ASNode sy = new ASNode(ASNode.Type.SYMBOL_LITERAL);
                AST.addVertex(sy);
                AST.addEdge(parentStack.peek(), sy);
                parentStack.push(sy);
                visit(ctx.function_name_or_symbol(i));
                parentStack.pop();
            }
            return null;
        }

        @Override
        public String visitExprAssign(RubyParser.ExprAssignContext ctx) {
            ASNode assign = new ASNode(ASNode.Type.ASSIGNMENT);
            assign.setLineOfCode(ctx.assignOperator().getStart().getLine());
            assign.setCode(ctx.assignOperator().getStart().getText());
            AST.addVertex(assign);
            AST.addEdge(parentStack.peek(), assign);
            parentStack.push(assign);
            visit(ctx.expr(0));
            visit(ctx.expr(1));
            parentStack.pop();
            return null;
        }

        @Override
        public String visitExprBatchAssign(RubyParser.ExprBatchAssignContext ctx) {
            ASNode assign = new ASNode(ASNode.Type.ASSIGNMENT);
            assign.setLineOfCode(ctx.ASSIGN().getSymbol().getLine());
            assign.setCode(ctx.ASSIGN().getText());
            AST.addVertex(assign);
            AST.addEdge(parentStack.peek(), assign);
            parentStack.push(assign);
            visit(ctx.expr(0));
            visit(ctx.expr(1));
            parentStack.pop();
            return null;
        }

        @Override
        public String visitExprRange(RubyParser.ExprRangeContext ctx) {
            ASNode range = new ASNode(ASNode.Type.RANGE);
            AST.addVertex(range);
            AST.addEdge(parentStack.peek(), range);
            parentStack.push(range);
            visit(ctx.expr(0));
            ASNode dot = new ASNode(ASNode.Type.SYMBOL);
            dot.setLineOfCode(ctx.getStart().getLine());
            if (ctx.DOT2() != null)
                dot.setCode("..");
            else if (ctx.DOT3() != null) {
                dot.setCode("...");
            }
            AST.addVertex(dot);
            AST.addEdge(parentStack.peek(), dot);
            visit(ctx.expr(1));
            parentStack.pop();
            return null;
        }

        @Override
        public String visitVarPathLiteral(RubyParser.VarPathLiteralContext ctx) {
            visitChildren(ctx);
            if (!Done) {
                ASNode var = new ASNode(ASNode.Type.CONST);
                var.setLineOfCode(ctx.getStart().getLine());
                var.setCode(ctx.getText());
                AST.addVertex(var);
                AST.addEdge(parentStack.peek(), var);
            } else
                Done = false;
            return null;
        }

        @Override
        public String visitInstanceVar(RubyParser.InstanceVarContext ctx) {
            ASNode var = new ASNode(ASNode.Type.VAR_FIELD);
            var.setLineOfCode(ctx.getStart().getLine());
            var.setCode(ctx.getText());
            String normalized;
            normalized = normalizedIdentifier(ctx.getText());
            if (normalized == null) {
                ++class_fieldsCounter;
                normalized = "$VARF_" + class_fieldsCounter;
                Gvars.put(ctx.getText(), normalized);
            }
            var.setNormalizedCode(normalized);
            AST.addVertex(var);
            AST.addEdge(parentStack.peek(), var);
            Done = true;
            return ctx.getText();
        }

        @Override
        public String visitGlobalVar(RubyParser.GlobalVarContext ctx) {
            ASNode var = new ASNode(ASNode.Type.GLOBALVAR);
            var.setLineOfCode(ctx.getStart().getLine());
            var.setCode(ctx.getText());
            String normalized;
            normalized = normalizedIdentifier(ctx.getText());
            if (normalized == null) {
                ++GvarsCount;
                normalized = "$VARG_" + GvarsCount;
                Gvars.put(ctx.getText(), normalized);
            }
            var.setNormalizedCode(normalized);
            AST.addVertex(var);
            AST.addEdge(parentStack.peek(), var);
            Done = true;
            return ctx.getText();
        }

        @Override
        public String visitString(RubyParser.StringContext ctx) {
            ASNode str = new ASNode(ASNode.Type.STRING);
            str.setLineOfCode(ctx.getStart().getLine());
            str.setCode(ctx.getText());
            AST.addVertex(str);
            AST.addEdge(parentStack.peek(), str);
            Done = true;
            return ctx.getText();

        }

        @Override
        public String visitVarPathId(RubyParser.VarPathIdContext ctx) {
            visitChildren(ctx);
            if (!Done) {
                if (!isIdent) {
                    ASNode var = new ASNode(ASNode.Type.VARIABLE);
                    var.setLineOfCode(ctx.getStart().getLine());
                    var.setCode(ctx.getText());
                    String normalized;
                    normalized = normalizedIdentifier(ctx.getText());
                    if (normalized == null) {
                        if (ismethod) {
                            ++varsCounter;
                            normalized = "$VARL_" + varsCounter;
                            vars.put(ctx.getText(), normalized);
                        } else {
                            ++mainCounter;
                            normalized = "$VARM_" + mainCounter;
                            Main.put(ctx.getText(), normalized);
                        }
                    }

                    var.setNormalizedCode(normalized);
                    AST.addVertex(var);
                    AST.addEdge(parentStack.peek(), var);
                } else {
                    ASNode var = new ASNode(ASNode.Type.IDENT);
                    var.setLineOfCode(ctx.getStart().getLine());
                    var.setCode(ctx.getText());
                    AST.addVertex(var);
                    AST.addEdge(parentStack.peek(), var);
                }
            } else
                Done = false;
            return ctx.getText();
        }

        private String normalizedIdentifier(String id) {
            String normalized = vars.get(id);
            if (normalized == null || normalized.isEmpty())
                normalized = class_fields.get(id);
            if (normalized == null || normalized.isEmpty())
                normalized = methods.get(id);
            if (normalized == null || normalized.isEmpty())
                normalized = Main.get(id);
            if (normalized == null || normalized.isEmpty())
                normalized = Gvars.get(id);
//            if (normalized == null || normalized.isEmpty())
//                normalized = id;
            return normalized;
        }

    }

}
